<?php

/**
 * @package mikrokosmos
 * mk swf library file created on  nov-2004
 * there are some tag definitions
 * "low level" function utilites and misc tag/item management
 * jaco_at_pixeldump_dot_org
 */


//---- SWF tag list
// is used to build a look-up array to known supported tag
$swfTagList = array();

$swfTagList["PlaceObject"]		   = 0x04;
$swfTagList["PlaceObject2"]		   = 0x1A;
$swfTagList["RemoveObject"]		   = 0x05;
$swfTagList["RemoveObject2"]	   = 0x1C;
$swfTagList["ShowFrame"]		   = 0x01;
$swfTagList["SetBackgroundColor"]  = 0x09;
$swfTagList["FrameLabel"]		   = 0x2B;
$swfTagList["NamedAnchor"]		   = 0x2C;
$swfTagList["Protect"]			   = 0x18;
$swfTagList["End"]				   = 0x00;
$swfTagList["ExportAssets"]		   = 0x38;
$swfTagList["ImportAssets"]		   = 0x39;
$swfTagList["ImportAssets2"]	   = 0x47;
$swfTagList["EnableDebugger"]	   = 0x3A;
$swfTagList["EnableDebugger2"]	   = 0x40;
$swfTagList["DoActionTag"]		   = 0x0C;
$swfTagList["DoInitActionTag"]	   = 0x3B;
$swfTagList["DefineShape"]		   = 0x02;
$swfTagList["DefineShape2"]		   = 0x16;
$swfTagList["DefineShape3"]		   = 0x20;
$swfTagList["DefineShape4"]		   = 0x52;
$swfTagList["DefineBits"]		   = 0x06;
$swfTagList["JPEGTables"]		   = 0x08;
$swfTagList["DefineBitsJPEG2"]	   = 0x15;
$swfTagList["DefineBitsJPEG3"]	   = 0x23;
$swfTagList["DefineBitsLossless"]  = 0x14;
$swfTagList["DefineBitsLossless2"] = 0x24;
$swfTagList["DefineMorphShape"]	   = 0x2E;
$swfTagList["DefineFont"]		   = 0x0A;
$swfTagList["DefineFontInfo"]	   = 0x0D;
$swfTagList["DefineFontInfo2"]	   = 0x3E;
$swfTagList["DefineFont2"]		   = 0x30;
$swfTagList["DefineText"]		   = 0x0B;
$swfTagList["DefineText2"]		   = 0x21;
$swfTagList["DefineEditText"]	   = 0x25;
$swfTagList["DefineFontName"]	   = 0x58;
$swfTagList["DefineSound"]		   = 0x0E;
$swfTagList["StartSound"]		   = 0x0F;
$swfTagList["SoundStreamHead"]	   = 0x12;
$swfTagList["SoundStreamHead2"]	   = 0x2D;
$swfTagList["SoundStreamBlock"]	   = 0x13;
$swfTagList["DefineButton"]		   = 0x07;
$swfTagList["DefineButton2"]	   = 0x22;
$swfTagList["DefineButtonCxform"]  = 0x17;
$swfTagList["DefineButtonSound"]   = 0x11;
$swfTagList["DefineSprite"]		   = 0x27;
$swfTagList["DefineVideoStream"]   = 0x3C;
$swfTagList["VideoFrame"]		   = 0x3D;
$swfTagList["ScriptLimits"]		   = 0x41;

// swf8
$swfTagList["FileAttributes"]	   = 0x45;
$swfTagList["PlaceObject3"]		   = 0x46;
$swfTagList["MetaData"]			   = 0x4D;
$swfTagList["CSMTextSettings"]	   = 0x4A;
$swfTagList["DefineFont3"]		   = 0x4B;
$swfTagList["DefineFontAlignZones"] = 0x49;

// swf9
// see mk_lib_swf9_bulk.php for further code
$swfTagList["ProductInfo"]	   	   = 0x29; 	// flex2 product info
$swfTagList["AS3ClassNames"]	   = 0x4C;	// class names
$swfTagList["DoABC2"]			   = 0x52;	// AS3 bytecode
$swfTagList["AS3SceneNames"]	   = 0x56; 	// scene names
$swfTagList["DebugMode"]	   = 0x3F; 	// scene names



$headerTags = array( "FileAttributes",
					 "MetaData",
					 "SetBackgroundColor",
					 "ScriptLimits",
					 "Protect",
					 "EnableDebugger",
					 "EnableDebugger2",
					 "ProductInfo",
					 "DebugMode");


$blendModes = array();
$blendModes["normal"] = 0;
$blendModes["normal1"] = 1;
$blendModes["layer"] = 2;
$blendModes["multiply"] = 3;
$blendModes["screen"] = 4;
$blendModes["ligthen"] = 5;
$blendModes["darken"] = 6;
$blendModes["add"] = 7;
$blendModes["subtract"] = 8;
$blendModes["difference"] = 9;
$blendModes["inver"] = 10;
$blendModes["alpha"] = 11;
$blendModes["erase"] = 12;
$blendModes["overlay"] = 13;
$blendModes["hardlight"] = 14;

// build two look-up tables
$swfTransTag_luStr = ""; // look-up string for verify tag presence
$swfTransTag_luAr = array(); // look-up array to retrieve tag name knowing its tagtype


foreach ($swfTagList as $tag => $bc){
	$bcStr = "x" .$bc;
	$swfTransTag_luAr[$bcStr] = $tag;
	$swfTransTag_luStr .= $bcStr ."-" .$tag .",";
}


/**
 * check if given tagID is an "header tag""
 */
function is_headerTag($tagID){

	global $headerTags;
	$tagName = get_tagName($tagID);

	return in_array($tagName, $headerTags, true);
}

/**
* verifies if tag i supported by passing tag id (decimal)
*/
function is_supported_tag($tagID){

	global $swfTransTag_luStr, $swfTransTag_luAr;
	$strTagID = "x" .$tagID ."-";
	return (strpos($swfTransTag_luStr, $strTagID) === false) ? false : true;
}

/**
 * returns true | false if a given tagID
 * has item ID
 */
function has_itemID($tagID){

	$hti = array(2, 5, 6, 9, 7,
				 11, 13, 14, 15, 17,
				 20, 21, 22, 23,
				 32, 33, 34, 35, 36, 37, 39,
				 46, 48, 60, 61, 62, 73, 74, 75, 88);

	foreach($hti as $h)
		if($tagID == $h) return true;

	return false;
}

/**
* accept tag id and
* return a valid tag name, check if tag id is supported
*/
function get_tagName($tagID){

	global $swfTransTag_luStr, $swfTransTag_luAr;

	if(!is_supported_tag($tagID)) return false;

	$strTagID = "x" .$tagID;
	return $swfTransTag_luAr[$strTagID];
}

/**
* accept tag name
* return a valid tag id
*/
function get_tagID($tagName){

	global $swfTagList;
	return $swfTagList[$tagName];
}

/**
 * return blendmode Name from a given blendMode index
 */
function get_blendMode($bmIndex = 0) {

	global $blendModes;

	$bmKeys = array_keys($blendModes);

	return $bmKeys[$bmIndex];
}


// --- some generic utils

/**
 * generic, converts a color string to an array
 */
function get_color_array($col = "#000000", $alpha = 0xff, $useAlpha = false){

	if($col[0] == "#") $col = substr($col, 1);

	$c = hexdec($col);

	$cAr = array();
	$cAr["red"] = $c >> 0x10;
	$cAr["green"] = ($c >> 0x08) & 0xFF;
	$cAr["blue"] = $c & 0xFF;

	if($alpha != 0xff || $useAlpha){
		$cAr["alpha"] = $alpha;
	}

	return $cAr;
}

/**
 * generic, converts a color array to html string
 */
function get_color_string($cAr = array()){
	$colorStr = "#000000";

	if(count($cAr)){
		$colorStr = "#";

		$colorStr .= isset($cAr["red"])   ? get_bytehex($cAr["red"]) : "00";
		$colorStr .= isset($cAr["green"]) ? get_bytehex($cAr["green"]) : "00";
		$colorStr .= isset($cAr["blue"])  ? get_bytehex($cAr["blue"]) : "00";
	}

	return $colorStr;
}

/**
 * generic, convert a byte value to a 2digit hex string
 */
function get_bytehex($val){

	$hex = dechex($val);
	if(strlen($hex) < 2) $hex = "0" .$hex;

	return $hex;
}


/**
* parameter: number
* converts in twips and
* return minimun number of bits needed to write
*/
function find_minBits($nmbAr, $inTwips = true){

	$tmpAr = array();

	if(is_array($nmbAr)){
		foreach($nmbAr as $val){
			if(!$inTwips) array_push($tmpAr, abs(nmb2twip($val)));
			else array_push($tmpAr, abs($val));
		}
		$minBits = strlen(decbin(max($tmpAr))) +1;
	}
	else $minBits = strlen(decbin($nmbAr)) +1;


	return $minBits;
}

/**
* parameter: number
* return bits needed to write
*/
function boundBits($num){ return strlen(decbin($num)); }

/**
 * count zeroes or one in a given number
 */
function count_bit($nmb, $bit = false){

	$nmbStr = decbin($nmb);
	$cmp = ($bit) ? "1" : "0";
 	$cb = 0;
 	$nLength = strlen($nmbStr);

	for($i = 0; $i < $nLength; $i++){
		$cb += ($nmbStr[$i] == $cmp) ? 1 : 0;
	}

	return $cb;
}

/**
* converts number to twip
*/
function nmb2twip($nmb){

	$nTwips = floor($nmb * TWIP);
	if($nTwips > MAX_TWIPS) $nTwips = MAX_TWIPS;
	return $nTwips;
}

/**
* "snap" number value to twip, it eases player rendering
*/
function snap2twip($nmb){

	$snap = floor($nmb * TWIP) / TWIP;
	$snap = floor($nmb * 100) / 100;
	$snap = (float) number_format($snap, 2, '.', '');
	return $snap;
}

/**
* converts twips to number
*/
function twip2nmb($tw){ return $tw  / TWIP; }

/**
* parameter: number
* return 2 bytes little-endian
*/
function swf_word($w) { return pack('v', $w); }

/**
 * parameter: number
 * return 8 bytes little-endian
 */
function swf_dword($w) { return pack('V', $w); }

function swf_double($d){
	$v = pack('d', $d);

	if(!CPU_ENDIAN){
		return $v;
	}
	else {
		$c = $v[3];
		$c .= $v[2];
		$c .= $v[1];
		$c .= $v[0];

		$c .= $v[7];
		$c .= $v[6];
		$c .= $v[5];
		$c .= $v[4];

		return strrev($c);
	}
}

/**
 * parameter: string
 * return string + a null byte terminator
 */
function swf_string($str) { return $str .chr(0x00); } // string with a null terminating byte


/**
 * return a bytes value from a binary string
 */
function swf_bin($bin) {

	$buf = "";

	while (strlen($bin) % 8 != 0) $bin .= "0";

	$steps = strlen($bin) / 8;

	for ($i = 0; $i < $steps; $i++)
		$buf .= chr(bindec(substr($bin, $i * 8, 8)));

	return $buf;
}


/**
 * return a binary string with fixed bitlength
 * from a numeric value
 */
function swf_value2bin($v, $bits) {
	return substr(sprintf("%0${bits}b", $v), -$bits);
}

/**
 * converts 1 byte data to number;
 */
function ui8dec($binUI8){
	return ord($binUI8);
	//return hexdec(bin2hex($binUI8));
}

/**
 * converts 2 bytes little-endian data to unsigned number;
 */
function ui16dec($binUI16){
	return hexdec(bin2hex(strrev($binUI16)));
}

/**
 * converts 4 bytes little-endian data to unsigned number;
 */
function ui32dec($binUI32){
	return hexdec(bin2hex(strrev($binUI32)));
}

/**
 * converts 1 byte data to signed number;
 */
function si8dec($binSI8){ return bin2dec(get_binData_string($binSI8)); }


/**
 * converts 2 bytes little-endian data to signed number;
 */
function si16dec($binSI16){
	//return bin2dec(get_binData_string(strrev($binSI16)));
	$u = unpack("s1", $binSI16);
	return $u[1];
}

/**
 * converts 4 bytes little-endian data to signed number;
 */
function si32dec($binSI32){ return bin2dec(get_binData_string(strrev($binSI32))); }


/**
 * converts 8 bytes little-endian data to signed float;
 */
function si64double($binSI64){

	$r = $binSI64[3];
	$r .= $binSI64[2];
	$r .= $binSI64[1];
	$r .= $binSI64[0];

	$r .= $binSI64[7];
	$r .= $binSI64[6];
	$r .= $binSI64[5];
	$r .= $binSI64[4];

	if(CPU_ENDIAN) $r = strrev($r);
	$bVal = unpack("d", $r);

	return (double) $bVal[1];;
}

/**
 * converts a float to 16.16 fixed point value
 */
function fp_value2bin($fv, $minBits = 32){

	$fvL = floor($fv);
	$fvR = $fv - $fvL;

	$fvLBinStr = swf_value2bin($fvL, 16);
	$fvRBinStr = "";

	$d = $fvR;

	for($i = 0;  $i < 16; $i++){

		$d *= 2;

		if($d >= 1){
			$fvRBinStr .= "1";
			$d -= 1;
		}
		else $fvRBinStr .= "0";
	}

	$fp32BinStr = $fvLBinStr .$fvRBinStr;

 	return substr($fp32BinStr, -$minBits);
}

/**
 * converts a string representing binary 16.16 fixed point
 * to float
 */
function fp_binStr_float($fpStr, $precision = 4, $fp = 16){

	$fvL = 0;
	$fvR = 0;

	if(!strlen($fpStr)) $fpStr = "0";

	$fps32 = substr(str_repeat($fpStr[0], $fp * 2), 0, $fp * 2 - strlen($fpStr)) .$fpStr;
	$fvLBinStr = substr($fps32, 0, $fp);
	$fvRBinStr = substr($fps32, $fp);

	for($i = 0; $i < $fp; $i++)
		if($fvRBinStr[$i] == "1")$fvR += pow(2, -($i + 1));

	$fvL = bin2dec($fvLBinStr);

	$mul = pow(10, $precision);
	$fvR = round($fvR * $mul) / $mul;

	return $fvL + $fvR;
}

/**
 * find minBits for fp 16.16 values
 * these valued are not related to twips
 * so inTwips flag is not used
 */
function fp_find_minBits($fpAr){

	$fpMinBits = 17; // 1.16 minimum bits

	if(!is_array($fpAr)){
		$tmpVal = $fpAr;
		$fpAr = array($tmpVal);
	}
	if(!max($fpAr)) return $fpMinBits;
	$fpStrAr = array();

	foreach($fpAr as $fpVal){

		$fi = count($fpStrAr);
		$fpStr = fp_value2bin($fpVal);

		$fc = ($fpStr[0] == "1") ? "0" : "1";
		$fpL = substr($fpStr, strpos($fpStr, $fc));

		$fpStrAr[$fi] = strlen($fpL);
	}

	$fpMinBits = max($fpStrAr) + 1;
	if($fpMinBits < 16) $fpMinBits = 16;

	return $fpMinBits;
}

/**
 * converts a 16bit bulk value to FLOAT16
 * supported from swf8
 * TEST CODE, not yet verified
 */
function read_float16($val) {

	$f16Str = get_binData_string($val);

	$s = -((int) $f16Str[15] - 1);
	$expStr = substr($f16Str, 1, 5);
	$mantissaStr = substr($f16Str, 6);

	$exp = bindec($expStr) - 16;
	$r = bindec(substr($mantissaStr, 0, $exp));
	$fpbStr = substr($mantissaStr, $exp, strrpos($mantissaStr, "0"));
	$fLength = strlen($fpbStr) - 1;

	for($i = $fLength, $m = 0; $i >= 0; $i--){
		$c = (int) $fpbStr[$i];
		$m += ($m + $c) / 2;
	}

	return ($r + $m) * $s;
}

/**
 * converts "binary string" to signed integer
 */
function bin2dec($buf){

	if(!isnegative($buf)) return bindec($buf);

	$r = substr($buf, strpos($buf, "0"));
	$rLength = strlen($r);

	$n = "";
	for($i = 0 ; $i < $rLength; $i++){
		$b = $r[$i];
		$n .= ($b == "0") ? "1" : "0";
	}

	return -(1 + bindec($n));
}

/**
 * return true | false
 * if given binary string starts with "1";
 */
function isnegative($binStr){ return ($binStr[0] == "1") ? true : false; }

/**
 * converts a raw binData to "binary" string
 * each bit is represented by "1" or "0"
 */
function get_binData_string($buf){

	$bufStr = "";
	$bLength = strlen($buf);

	for($i = 0; $i < $bLength; $i++)
		$bufStr .= swf_value2bin(ui8dec($buf[$i]), 8);

	return $bufStr;
}

/**
 * converts a "binary" string whose bit is represented by "1" or "0"
 * to a binary data
 */
function get_binData($binStr, $requirePad = false){

	if(strlen($binStr) % 8 && !$requirePad) {
		$rfLength = floor(strlen($binStr) / 8) * 8;
		$binStr = substr($binStr, 0, $rfLength);
	}

	return swf_bin($binStr);
}

/**
 * private:
 * compute padding bits from a given nBits
 * return a padding "0" string
 */
function get_pad($nBits) { return substr("00000000", 0 , (8 - $nBits % 8) & 7); }


function set_global_swfVersion($swfVersion = 8){
	global $workingSwfVersion;
	$workingSwfVersion = $swfVersion;
}

function get_global_swfVersion(){
	global $workingSwfVersion;
	return $workingSwfVersion;
}

// -------------------------------------------------------

/**
 * parameters, coordx, coordy, width, height
 * return a Flash rectangle structure
 */
function swf_rect($x, $y, $wd, $hg) {
	$minBits = find_minBits(array($x,$y,$wd,$hg), false);
	//$pad = 8 - ((5 + $minBits * 4) % 8);
	//if($pad == 8) $pad = 0;
	$pad = strlen(get_pad(5 + $minBits * 4));

	return swf_bin(	sprintf('%05b', $minBits).
			swf_value2bin(nmb2twip($x), $minBits).
			swf_value2bin(nmb2twip($wd), $minBits).
			swf_value2bin(nmb2twip($y), $minBits).
			swf_value2bin(nmb2twip($hg), $minBits).
			swf_value2bin(0, $pad));
}

/**
 * return short tag header record
 * less than 63 bytes in length
 */
function SWFTag_short_header($tagID, $length) {
	return swf_word($tagID << 6 | ($length & 0x3f));
}

/**
 * return long tag header record
 * more or equal than 63 bytes in length
 */
function SWFTag_long_header($tagID, $tLength) {
	return swf_word($tagID << 6 | 0x3f) .swf_dword($tLength);
}


/**
 * parameters, stageWidth, stageHeight, fps, numFrames
 * build and return the last part of swf header
 */
function swf_setTailheader($w, $h, $rate, $frames) {
	$result = swf_rect(0,0,$w,$h);
	$result .= NIL;
	$result .= chr($rate);
	$result .= swf_word($frames);
	return $result;
}


/**
 * parameters, red, green, blue components > 0 < 255;
 * build and return a setBackgroundColor tag
 */
function swf_setBgcolor($r, $g, $b) {
	global $swfTagList;
	return SWFTag_short_header($swfTagList["SetBackgroundColor"], 3) .chr($r) .chr($g) .chr($b);
}


/**
 * parameters: they are in dev
 * starts defining shape
 */
function swf_init_defineShape($shapeID,$tag_length,$xmin,$ymin,$xmax,$ymax){
	global $swfTagList;

	$iDs = SWFTag_long_header(2,$tag_length).
			  swf_word($shapeID).
			  swf_rect($xmin,$ymin, $xmax, $ymax);
	return $iDs;
}

/**
 * parameters: array of fillstyles
 * builds an return fillstyles record
 * NOTE: this is an obsolete and incomplete function
 */
function swf_fillStyles($fillAr = Array()){

	if(!count($fillAr))	return NIL;

	$fillStylecount = chr(count($fillAr));
	$fillRecords = "";

	foreach($fillAr as $fillStyleColor){
		$fillRecords .= NIL;	// solid fill
		$fillRecords .= chr($fillStyleColor["red"]);
		$fillRecords .= chr($fillStyleColor["green"]);
		$fillRecords .= chr($fillStyleColor["blue"]);
	}
	return $fillStylecount .$fillRecords;
}


/**
 * parameters: array of linewidths, array of linestyles
 * builds an return linestyles record
 * NOTE: this is an obsolete and incomplete function
 */
function swf_lineStyles($lineWidths = Array(), $lineAr = Array()){
	if(!count($lineWidths))
		return NIL;
	$lineStylecount = chr(count($lineWidths));
	$lineRecords = "";

	$i = 0;

	foreach($lineAr as $lineStyleColor){
		$lineRecords .= swf_word($lineWidths[$i]);
		$lineRecords .= chr($lineStyleColor["red"]);
		$lineRecords .= chr($lineStyleColor["green"]);
		$lineRecords .= chr($lineStyleColor["blue"]);
		$i++;
	}
	return $lineStylecount . $lineRecords;
}

/**
 * parameters: fillstyle record, linestyle record
 * builds an return shapewithstyle record
 */
function swf_shape_with_style($nFillStyles, $nLineStyles){
	$nFillBits = boundBits($nFillStyles);
	$nLineBits = boundBits($nLineStyles);

	if(!$nFillStyles) $nFillBits = 0;
	if(!$nLineStyles) $nLineBits = 0;

	$FillLineBits = chr(swf_value2bin($nFillStyles,4) . swf_value2bin($nLineStyles, 4));
	return $FillLineBits;
}

/**
 * parameters: x, y, width, height
 * builds an return shaperecord rect structure
 */
function swf_shape_record_rect($x,$y,$wd,$hg){

	$mtdeltaX = nmb2twip($x+$wd);
	$mtdeltaY = nmb2twip($y+$hg);

	$nBits_wd = find_minBits(array(-$wd), false);
	$nBits_hg = find_minBits(array(-$hg), false);

	$nBits = max($nBits_wd, $nBits_hg);

	//$pad = 8 - ((($nBits_wd - 2)* 2 + ($nBits_hg - 2)* 2 + 13) % 8);
	//if($pad == 8) $pad = 0;
	$pad = strlen(get_pad((($nBits_wd - 2)* 2 + ($nBits_hg - 2)* 2 + 13)));

	// shape record type 1, init
	// 0 non edge record
	// 00100 fillstyle 1 change flag
	// 1 fillstyle index
	// 1 subsequent edge records flag
	$ssr = swf_bin( sprintf('%01b', 0).
			sprintf('%01b', 0).
			sprintf('%01b', 0).
			sprintf('%01b', 1).
			sprintf('%01b', 0).
			sprintf('%01b', 0).

			//sprintf('%05b', $nBits).
			//swf_value2bin($mtdeltaX, $nBits).
			//swf_value2bin($mtdeltaY, $nBits).

			sprintf('%01b', 1).

			sprintf('%01b', 1).
			sprintf('%01b', 1).
			swf_value2bin($nBits_wd - 2, 4).
			sprintf('%01b', 0).
			sprintf('%01b', 0).
			swf_value2bin($mtdeltaX, $nBits_wd).

			sprintf('%01b', 1).
			sprintf('%01b', 1).
			swf_value2bin($nBits_hg - 2, 4).
			sprintf('%01b', 0).
			sprintf('%01b', 1).
			swf_value2bin($mtdeltaY, $nBits_hg).

			sprintf('%01b', 1).
			sprintf('%01b', 1).
			swf_value2bin($nBits_wd - 2, 4).
			sprintf('%01b', 0).
			sprintf('%01b', 0).
			swf_value2bin(-$mtdeltaX, $nBits_wd).

			sprintf('%01b', 1).
			sprintf('%01b', 1).
			swf_value2bin($nBits_hg - 2, 4).
			sprintf('%01b', 0).
			sprintf('%01b', 1).
			swf_value2bin(-$mtdeltaY, $nBits_hg).

			swf_value2bin(0, 6).
			swf_value2bin(0, $pad));

	return $ssr;
}


/**
 * parameters: shape id (number)
 * builds an return placeobject2 tag
 */
function swf_placeObject2($shapeID = 1,
						  $depthID = 1,
						  $placeFlagMove = false,
						  $placeFlagHasCharacter = true,
						  $placeFlagHasMatrix = true){ // display shapeID object

	global $swfTagList;

	$poh = ($placeFlagMove) ? 1 : 0;
	$poh += ($placeFlagHasCharacter) ? 2 : 0;
	$poh += ($placeFlagHasMatrix) ? 4 : 0;
	$mo = ($placeFlagHasMatrix) ? NIL : "";

	$poBody = chr($poh).
			swf_word($depthID).
			swf_word($shapeID).
			$mo;

	$pO = SWFTag_short_header($swfTagList["PlaceObject2"], strlen($poBody)). $poBody;

	return $pO;
}

/**
 * builds an return showframe tag
 * this is the last tag in every frame
 */
function swf_showFrame() { // display current frame
	global $swfTagList;
	return SWFTag_short_header($swfTagList["ShowFrame"], 0);
}

/**
 * builds an return end tag
 * place this at end of the movie
 */
function SWFTag_end() { // end of SWF file
	global $swfTagList;
	return SWFTag_short_header($swfTagList["End"],0);
}


//--------------------------------------------------
// READ functions
//--------------------------------------------------

/**
 * read rawdata
 * returns an array containing these fields:
 * tagID -> tag code id
 * tagLength -> body length
 * itemID -> item id, even if data does non contains
 * tagOffset -> used to check short/long tag header
 * tagType -> tag description
 */
function read_tag_header_data($buf, $offsetPosition = -1){

	global $swfTransTag_luStr, $swfTransTag_luAr;

	$tagHData = array();

	$rh = swf_value2bin(ui8dec($buf[1]), 8);
	$rh .= swf_value2bin(ui8dec($buf[0]), 8);

	$tagIDStr = substr($rh, 0, 10);
	$shLength = substr($rh, 10);
	$sh = bindec($shLength);

	$tagHData["tagID"] = bindec($tagIDStr);
	$tagHData["tagLength"] = $sh;

	$tagHData["offset"] = $offsetPosition;
	$tagHData["itemID"] = 0;

	$tagHData["itemID"] = ui16dec(substr($buf, 2, 2));

	$tagHData["tagOffset"] = 2 + $sh;
	$tagHData["tagSL"] = 2; // short tag

	if($sh >= 63){
		$storedTagSize = ui32dec(substr($buf, 2, 4));
		$tagHData["tagLength"] = $storedTagSize;
		$tagHData["tagOffset"] = 6 + $storedTagSize;
		$tagHData["offset"] = $tagHData["tagOffset"];
		$tagHData["tagSL"] = 6; // long tag
		$tagHData["itemID"] = ui16dec(substr($buf, 6, 2));
	}

	if(has_itemID($tagHData["tagID"]))
		$tagHData["itemID"] = ui16dec(substr($buf, $tagHData["tagSL"], 2));

	$tmpbc = "x" . $tagHData["tagID"];

	if(strpos($swfTransTag_luStr, $tmpbc ."-") === false){
		$tagHData["tagType"] = $tagHData["tagID"];
	}
	else $tagHData["tagType"] = $swfTransTag_luAr[$tmpbc];

	$tagHData["buf"] = substr($buf, 0, $tagHData["tagSL"]);

	return $tagHData;
}


/**
 * fill and return an array
 * containing RECT fields from a given byte buffer
 */
function read_rect_data($buf){

	$rect = array();
	$ofst = 5;

	$bufStr = get_binData_string($buf);

	$nBits = bindec(substr($bufStr, 0, $ofst));

	$trBits = $ofst + $nBits * 4;
	if($trBits < strlen($buf) / 8) return -1; // error: too few data

	$rect["nBits"] = $nBits;
	$rect["xmin"] = bin2dec(substr($bufStr, $ofst, $nBits));
	$ofst += $nBits;
	$rect["xmax"] = bin2dec(substr($bufStr, $ofst, $nBits));
	$ofst += $nBits;
	$rect["ymin"] = bin2dec(substr($bufStr, $ofst, $nBits));
	$ofst += $nBits;
	$rect["ymax"] = bin2dec(substr($bufStr, $ofst, $nBits));
	$ofst += $nBits;

	//$rect["pad"] = strlen(substr("00000000", $ofst % 8));
	//	if($rect["pad"] >= 8) $rect["pad"] = 0;
	$rect["pad"] = strlen(get_pad($ofst));

	$rect["rectLength"] = ($ofst + $rect["pad"]) / 8;

	return $rect;
}


/**
 * reads data
 * returns a string, strip away null-termination char
 */
function read_swf_string($buf, $fixSlashes = false){

	if($buf[0] == NIL) return "";

	$str = (substr($buf, 0, strpos($buf, NIL)));

	if($fixSlashes){
		$spChars = array("\r", "\n");
		$spSlashes = array("\\r", "\\n");
		$str = str_replace($spChars, $spSlashes, $str);
	}

	return $str;
}

/**
 * fill and return an array
 * containing MATRIX fields from a given byte buffer
 */
function read_matrix_data($buf){

	if(!strlen($buf)) return -1; // error, no data available

	$bufStr = get_binData_string($buf);
	$ofst = 0;
	$md = array();

	// assign default values
	$md["hasScale"] = 0; // hasScale
	$md["nScaleBits"] = 0; // 22 scaleBits
	$md["scaleX"] = 0.0; // scale = 20 / 1 -> 1 -> 100$
	$md["scaleY"] = 0.0; // ... the same ...
	$md["hasRotate"] = 0; // no rotate
	$md["nRotateBits"] = 0; // ...
	$md["rotateSkew0"] = 0; // ...
	$md["rotateSkew1"] = 0; // ...
	$md["nTranslateBits"] = 1; // this field is required
	$md["translateX"] = 0; // moveTo 0
	$md["translateY"] = 0; // and 0
	$md["matrixLength"] = 0;

	$md["hasScale"] = (int) $bufStr[0];
	$ofst++;

	if($md["hasScale"]){

		$nScaleBits = bindec(substr($bufStr, $ofst, 5));
		$md["nScaleBits"] = $nScaleBits;
		$ofst+= 5;

		$scaleXStr = substr($bufStr, $ofst, $md["nScaleBits"]);

		$md["scaleX"] = fp_binStr_float($scaleXStr);
		$ofst+= $md["nScaleBits"];


		$scaleYStr = substr($bufStr, $ofst, $md["nScaleBits"]);
		$md["scaleY"] = fp_binStr_float($scaleYStr);
		$ofst+= $md["nScaleBits"];
	}

	$hasRotate = substr($bufStr, $ofst, 1);
	$md["hasRotate"] = ($hasRotate == "1")? true : false;
	$ofst++;

	if($md["hasRotate"]){

		$nRotateBitsStr = substr($bufStr, $ofst, 5);
		$md["nRotateBits"] = bindec($nRotateBitsStr);
		$ofst+= 5;

		$rs0 = substr($bufStr, $ofst, $md["nRotateBits"]);
		$md["rotateSkew0"] = fp_binStr_float($rs0);
		$ofst+= $md["nRotateBits"];

		$rs1 = substr($bufStr, $ofst, $md["nRotateBits"]);
		$md["rotateSkew1"] = fp_binStr_float($rs1);
		$ofst+= $md["nRotateBits"];
	}

	$nTranslateBitsStr = substr($bufStr, $ofst, 5);

	$md["nTranslateBits"] = bindec($nTranslateBitsStr);
	$ofst+= 5;

	if($md["nTranslateBits"]){

		$translateXStr = substr($bufStr, $ofst, $md["nTranslateBits"]);
		$md["translateX"] = bin2dec($translateXStr);
		$ofst+= $md["nTranslateBits"];

		$translateYStr = substr($bufStr, $ofst, $md["nTranslateBits"]);
		$md["translateY"] = bin2dec($translateYStr);
		$ofst+= $md["nTranslateBits"];
	}

	$md["pad"] = strlen(get_pad($ofst));
	$md["matrixLength"] = ($ofst + $md["pad"]) / 8; // bytes length for matrix

	return $md;
}

/**
 * reads data
 * returns an associative array with these fields:
 * red, green, blue, [alpha]
 */
function read_color_data($buf, $dsType = SHAPE){

	$cAr = array();
	$cAr["red"] = ui8dec($buf[0]);
	$cAr["green"] = ui8dec($buf[1]);
	$cAr["blue"] = ui8dec($buf[2]);

	// please note that if buf is too short color is set to be complete opaque
	if($dsType == SHAPE2 || $dsType == SHAPE3)
		$cAr["alpha"] = (strlen($buf) < 4) ?  0xFF: ui8dec($buf[3]);

	return $cAr;
}


/**
 * reads data (CXFORM structure)
 * returns an associative array with these fields
 * hasAddTerms, hasMultTerms, nBits, redMultTerm, greenMultTerm,
 * blueMultTerm, redAddTerm, greenAddTerm, blueAddTerm
 */
function read_cxform_data($buf){

	$cfd = array();

 	$cfd["hasAddTerms"] 	= 0;
 	$cfd["hasMultTerms"] 	= 0;
 	$cfd["nBits"]		 	= 0;
 	$cfd["redMultTerm"]		= 0;
 	$cfd["greenMultTerm"]	= 0;
 	$cfd["blueMultTerm"]	= 0;
 	$cfd["redAddTerm"]		= 0;
 	$cfd["greenAddTerm"]	= 0;
 	$cfd["blueAddTerm"]		= 0;

	$binStr = get_binData_string($buf);
	$ofst = 0;

	if($binStr[0] == "1") $cfd["hasAddTerms"]  = 1;
	if($binStr[1] == "1") $cfd["hasMultTerms"] = 1;

	$ofst = 2;

	$nBits = bindec(substr($binStr, $ofst, 4));
	$cfd["nBits"] = $nBits;
	$ofst = 6;

	if($cfd["hasMultTerms"]){

		$cfd["redMultTerm"] = bin2dec(substr($binStr, $ofst, $nBits));
		$ofst += $nBits;
		$cfd["greenMultTerm"] = bin2dec(substr($binStr, $ofst, $nBits));
		$ofst += $nBits;
		$cfd["greenMultTerm"] = bin2dec(substr($binStr, $ofst, $nBits));
	}

	if($cfd["hasAddTerms"]){

		$cfd["redAddTerm"] = bin2dec(substr($binStr, $ofst, $nBits));
		$ofst += $nBits;
		$cfd["greenAddTerm"] = bin2dec(substr($binStr, $ofst, $nBits));
		$ofst += $nBits;
		$cfd["greenAddTerm"] = bin2dec(substr($binStr, $ofst, $nBits));
	}

	return $cfd;
}


/**
 * reads data (CXFORMWITHALPHA structure)
 * returns an associative array with these fields
 * hasAddTerms, hasMultTerms, nBits, redMultTerm, greenMultTerm,
 * blueMultTerm, alphaMultTerm, redAddTerm, greenAddTerm, blueAddTerm, alphaAddTerm
 */
function read_cxform_with_alpha_data($buf = ""){

	if(!strlen($buf)) return false;

	$cwad = array();

 	$cwad["hasAddTerms"] 	= 0;
 	$cwad["hasMultTerms"] 	= 0;
 	$cwad["nBits"]		 	= 0;
 	$cwad["redMultTerm"]	= 0;
 	$cwad["greenMultTerm"]	= 0;
 	$cwad["blueMultTerm"]	= 0;
 	$cwad["alphaMultTerm"]	= 0;
 	$cwad["redAddTerm"]		= 0;
 	$cwad["greenAddTerm"]	= 0;
 	$cwad["blueAddTerm"]	= 0;
 	$cwad["alphaAddTerm"]	= 0;

	$binStr = get_binData_string($buf);
	$ofst = 0;

	$cwad["hasAddTerms"]  = (int) $binStr[0];
	$cwad["hasMultTerms"] = (int) $binStr[1];

	$ofst = 2;

	$nBits = bindec(substr($binStr, $ofst, 4));
	$cwad["nBits"] = $nBits;
	$ofst = 6;

	if($cwad["hasMultTerms"]){

		$cwad["redMultTerm"]   = fp_binStr_float(substr($binStr, $ofst, $nBits), 4, 8);
		$ofst += $nBits;
		$cwad["greenMultTerm"] = fp_binStr_float(substr($binStr, $ofst, $nBits), 4, 8);
		$ofst += $nBits;
		$cwad["blueMultTerm"] = fp_binStr_float(substr($binStr, $ofst, $nBits), 4, 8);
		$ofst += $nBits;
		$cwad["alphaMultTerm"] = fp_binStr_float(substr($binStr, $ofst, $nBits), 4, 8);
		$ofst += $nBits;
	}

	if($cwad["hasAddTerms"]){

		$cwad["redAddTerm"]   = fp_binStr_float(substr($binStr, $ofst, $nBits), 4, 8);
		$ofst += $nBits;
		$cwad["greenAddTerm"] = fp_binStr_float(substr($binStr, $ofst, $nBits), 4, 8);
		$ofst += $nBits;
		$cwad["blueAddTerm"] = fp_binStr_float(substr($binStr, $ofst, $nBits), 4, 8);
		$ofst += $nBits;
		$cwad["alphaAddTerm"] = fp_binStr_float(substr($binStr, $ofst, $nBits), 4, 8);
		$ofst += $nBits;
	}

	$cwad["pad"] = strlen(get_pad($ofst));
	$cwad["cxformLength"] = ($ofst + $cwad["pad"]) / 8; // bytes length for cxformwithalpha

	return $cwad;
}

/**
 * reads data
 * fill an associative array with these fields
 *
 * pfHasClipActions
 * pfHasClipDepth
 * pfHasName
 * pfHasRatio
 * pfHasColorTransform
 * pfHasMatrix
 * pfHasCharacter
 * pfMove
 *
 * depth
 * characterId / itemID
 * matrix
 * colorTransform
 * ratio
 * name
 * clipDepth
 * clipActions
 *
 * this is a complex object, refer to swf file format
 * for further info
 */
function read_placeObject2_data($tagData){

	$pod = array();
	$tagHD = read_tag_header_data(substr($tagData, 0, 10));
	$ofst = $tagHD["tagSL"];

	$poHead = get_binData_string($tagData[$ofst]);

	$pod["pfHasClipActions"] 	= $poHead[0] == "1" ? 1 : 0;
	$pod["pfHasClipDepth"]	 	= $poHead[1] == "1" ? 1 : 0;
	$pod["pfHasName"]		 	= $poHead[2] == "1" ? 1 : 0;
	$pod["pfHasRatio"]		 	= $poHead[3] == "1" ? 1 : 0;
	$pod["pfHasColorTransform"] = $poHead[4] == "1" ? 1 : 0;
	$pod["pfHasMatrix"]		 	= $poHead[5] == "1" ? 1 : 0;
	$pod["pfHasCharacter"]	 	= $poHead[6] == "1" ? 1 : 0;
	$pod["pfMove"]			 	= $poHead[7] == "1" ? 1 : 0;

	$ofst++;
	$pod["depth"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;

	if($pod["pfHasCharacter"]){
		$pod["characterID"] = ui16dec(substr($tagData, $ofst, 2));
		$ofst += 2;
	}

	if($pod["pfHasMatrix"]){
		$pod["matrix"] = read_matrix_data(substr($tagData, $ofst));
		$ofst += $pod["matrix"]["matrixLength"];
	}

	if($pod["pfHasColorTransform"]){
		$pod["colorTransform"] = read_cxform_with_alpha_data(substr($tagData, $ofst));
		$ofst += $pod["colorTransform"]["cxformLength"];
	}

	if($pod["pfHasRatio"]){
		$pod["ratio"] = ui16dec(substr($tagData, $ofst, 2));
		$ofst += 2;
	}

	if($pod["pfHasName"]){
		$pod["name"] = read_swf_string(substr($tagData, $ofst));
		$ofst += strlen($pod["name"]) + 1;
	}

	if($pod["pfHasClipDepth"]){
		$pod["clipDepth"] = ui16dec(substr($tagData, $ofst, 2));
		$ofst += 2;
	}

	/* TO DO: implement [maybe] clipActions */
	if($pod["pfHasClipActions"]){
		//$pod["clipActions"] = read_clipActions_data(substr($tagData, $ofst));
		$pod["caBuf"] = substr($tagData, $ofst);
	}

	$pod["placeObject2Length"] = strlen($tagData);

	return $pod;
}

/**
 * reads data
 * returns an associative array with placeObjec2 flags
 */
function read_placeObject2_flags($poFlags){

	$pofData = array();
	$poHead = get_binData_string($poFlags);

	$pofData["pfHasClipActions"] 	= $poHead[0] == "1" ? true : false;
	$pofData["pfHasClipDepth"]	 	= $poHead[1] == "1" ? true : false;
	$pofData["pfHasName"]		 	= $poHead[2] == "1" ? true : false;
	$pofData["pfHasRatio"]		 	= $poHead[3] == "1" ? true : false;
	$pofData["pfHasColorTransform"] = $poHead[4] == "1" ? true : false;
	$pofData["pfHasMatrix"]		 	= $poHead[5] == "1" ? true : false;
	$pofData["pfHasCharacter"]	 	= $poHead[6] == "1" ? true : false;
	$pofData["pfMove"]			 	= $poHead[7] == "1" ? true : false;

	return $pofData;
}


/**
 * reads data
 * returns an associative array with placeObjec3 data
 * refer to read_placeObject2_data
 * it has some optional flags and fields:
 * 		$pfHasImage
 * 		$pfHasClassName
 * 		$pfHasCacheAsBitmap
 * 		$pfHasBlendMode
 * 		$pfHasFilterList
 *
 * 			their presence depends on placeflags Above
 * 		$className	// swf string
 * 		$filterList	// array of filter
 * 		$blendMode	// UI8
 *
 *  NOTE: there are some differences for swf8 and >= swf9
 */
function read_placeObject3_data($tagData) {

	$po3d = array();
	$po3d["swfVersion"] = get_global_swfVersion();

	$tagHD = read_tag_header_data(substr($tagData, 0, 10));
	$ofst = $tagHD["tagSL"];

	$poHead = get_binData_string(substr($tagData, $ofst, 2));

	$ofst += 2;

	$po3d["pfHasClipActions"] 	= $poHead[0] == "1" ? 1 : 0;
	$po3d["pfHasClipDepth"]	 	= $poHead[1] == "1" ? 1 : 0;
	$po3d["pfHasName"]		 	= $poHead[2] == "1" ? 1 : 0;
	$po3d["pfHasRatio"]		 	= $poHead[3] == "1" ? 1 : 0;
	$po3d["pfHasColorTransform"] = $poHead[4] == "1" ? 1 : 0;
	$po3d["pfHasMatrix"]	 	= $poHead[5] == "1" ? 1 : 0;
	$po3d["pfHasCharacter"]	 	= $poHead[6] == "1" ? 1 : 0;
	$po3d["pfMove"]			 	= $poHead[7] == "1" ? 1 : 0;

	// next three bits are reserved

	$po3d["pfHasImage"]	 		= $poHead[11] == "1" ? 1 : 0;
	$po3d["pfHasClassRef"]	 	= $poHead[12] == "1" ? 1 : 0;
	$po3d["pfHasCacheAsBitmap"]	= $poHead[13] == "1" ? 1 : 0;
	$po3d["pfHasBlendMode"]	 	= $poHead[14] == "1" ? 1 : 0;
	$po3d["pfHasFilterList"]	= $poHead[15] == "1" ? 1 : 0;

	$po3d["depth"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;

	if($po3d["pfHasClassRef"] || ($po3d["pfHasImage"] && $po3d["pfHasCharacter"])){
		$po3d["classRef"] = ui16dec(substr($tagData, $ofst, 2));
		$ofst += 2;
	}

	if($po3d["pfHasCharacter"] && $po3d["swfVersion"] < 9){
		$po3d["characterID"] = ui16dec(substr($tagData, $ofst, 2));
		$ofst += 2;
	}

	if($po3d["pfHasMatrix"]){
		$po3d["matrix"] = read_matrix_data(substr($tagData, $ofst));
		$ofst += $po3d["matrix"]["matrixLength"];
	}

	if($po3d["pfHasColorTransform"]){
		$po3d["colorTransform"] = read_cxform_with_alpha_data(substr($tagData, $ofst));
		$ofst += $po3d["colorTransform"]["cxformLength"];
	}

	if($po3d["pfHasRatio"]){
		$po3d["ratio"] = ui16dec(substr($tagData, $ofst, 2));
		$ofst += 2;
	}

	if($po3d["pfHasName"]){
		$po3d["name"] = read_swf_string(substr($tagData, $ofst));
		$ofst += strlen($po3d["name"]) + 1;
	}


	if($po3d["pfHasClipDepth"]){
		$po3d["clipDepth"] = ui16dec(substr($tagData, $ofst, 2));
		$ofst += 2;
	}


	if($po3d["pfHasFilterList"]){
		$po3d["filterList"] = read_filterList_data(substr($tagData, $ofst));
		$ofst += $po3d["filterList"]["flLength"];
	}
	$po3d["filterList"] = array();

	$po3d["blendMode"] = BLEND_NORMAL;

	if($po3d["pfHasBlendMode"]){
		$po3d["blendMode"] = ui8dec($tagData[$ofst++]);
	}

	/* TO DO: implement [maybe] clipActions */
	if($po3d["pfHasClipActions"]){
		//$po3d["clipActions"] = read_clipActions_data(substr($tagData, $ofst));
		$po3d["caBuf"] = substr($tagData, $ofst);
	}

	$po3d["placeObject3Length"] = strlen($tagData);

	return $po3d;
}

/**
 * >= swf8
 * returns an array of hardcoded filters
 * NOTE: not sure if this will be implemented
 */
function read_filterList_data($buf){

	/* TODO */
	/*
	$fld = array();
	$fCount = ui8dec($buf[0]);
	$ofst = 1;

	for($i = 0; $i < $fCount; $i++){
		$fd = read_filter_data(substr($buf, $ofst));
		$fld[] = $fd;
		$ofst += $fd["filterLength"];
	}

	$fld["flLength"] = $ofst;
	*/

	return "";
}

/**
 * >= swf8
 * return an array with hardcoded FILTER data
 * NOTE: not sure if this will be implemented
 */
function read_filter_data($buf){
	/* TODO */
	$fd = array();
	//$fd["filterLength"];
	return "";
}

/**
 * reads data
 * returns the only one removeOject2 field: depth
 */
function read_removeObject2_data($tagData){

	$tagHD = read_tag_header_data(substr($tagData, 0 , 10));
	$depth = ui16dec(substr($tagData, $tagHD["tagSL"] , 2));

	return $depth;
}

/**
 * reads data
 * fills and returns an associative array
 * with color fiels
 */
function read_setBackgroundColor_data($tagData){

	$tagHD = read_tag_header_data($tagData);
	$tagBody = substr($tagData, $tagHD["tagSL"]);

	$cAr = array();
	$cAr["red"]   = ui8dec($tagBody[0]);
	$cAr["green"] = ui8dec($tagBody[1]);
	$cAr["blue"]  = ui8dec($tagBody[2]);

	return $cAr;
}


/**
 * reads data
 * returns an associative array with these fields:
 * spriteID, frameCount, controlTags
 */
function read_movieClip_data($tagData){

	$mcd = array();
	$tagHeader = read_tag_header_data(substr($tagData, 0, 8));
	$ofst = $tagHeader["tagSL"];

	$mcd["itemID"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;
	$mcd["frameCount"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;
	$mcd["rawControlTags"] = substr($tagData, $ofst);

	return $mcd;
}

/**
 * reads data
 * returns the name label
 */
function read_frameLabel_data($tagData){

	$tagHD = read_tag_header_data(substr($tagData, 0, 10));
	$fld = array();
	$fld["label"] = read_swf_string(substr($tagData, $tagHD["tagSL"]));
	if($tagHD["tagLength"] > strlen($fld["label"] + 1));
	$fld["hasAnchor"] = 1;

	return $fld;
}

/**
 * reads data
 * return the only one field in protect tag
 * MD5password
 */
function read_protect_data($tagData){

	$tagHD = read_tag_header_data($tagData);
	$tagBody = substr($tagData, $tagHD["tagSL"] + 2);

	return read_swf_string($tagBody);
}

/**
 * reads data
 * fills and returns exportAssets (movieclip with linkage)
 * fields
 */
function read_exportAssets_data($tagData){

	$ead = array();

	$tagHD = read_tag_header_data(substr($tagData, 0, 10));
	$tagBody = substr($tagData, $tagHD["tagSL"]);
	$ofst = 0;
	$ead["assetCount"] = ui16dec(substr($tagBody, $ofst, 2));
	$ofst += 2;
	$ead["assets"] = array();

	for($i = 0; $i < $ead["assetCount"]; $i++){
		$ead["assets"][$i]["id"] = ui16dec(substr($tagBody, $ofst, 2));
		$ofst += 2;
		$ead["assets"][$i]["name"] = read_swf_string(substr($tagBody, $ofst));
		$ofst += strlen($ead["assets"][$i]["name"]) + 1;
	}

	return $ead;
}


/**
 * reads data
 * returns an associative array with FileAttributes
 * tag data from a give binary data
 */
function read_fileAttributes_data($tagData){

	$faData = array();
	$tagHD = read_tag_header_data(substr($tagData, 0, 4));
	$tagBody = substr($tagData, $tagHD["tagSL"]);
	$binStr = get_binData_string($tagBody[0]);

	$faData["containsMetaData"] = $binStr[3] == "1" ? true : false;
	$faData["containsAS3"]		= $binStr[4] == "1" ? true : false;
	$faData["suppressCDC"]		= $binStr[5] == "1" ? true : false;
	$faData["relativeURLs"]		= $binStr[6] == "1" ? true : false;
	$faData["lpSecurity"]		= $binStr[7] == "1" ? true : false;

	return $faData;
}


/**
 * reads data
 * returns a string containing metadata
 * notice: metadata is an xml instance content
 */
function read_metadata_data($tagData){

	$tagHD = read_tag_header_data(substr($tagData, 0, 4));
	$tagBody = substr($tagData, $tagHD["tagSL"]);
	$metadata = read_swf_string($tagBody);

	return $metadata;
}

/**
 * reads data
 * returns an associative array with BUTTON2
 * field data before BUTTONRECORD block data
 */
function read_defineButton_data($tagData = ""){

	if(!strlen($tagData)) return false; // avoid empty calls;

	$bd = read_tag_header_data(substr($tagData, 0, 10));
	$ofst = $bd["tagSL"] + 2;

	if($bd["tagID"] == BUTTON2){
		$buttonFlagsStr = get_binData_string($tagData[$ofst]);
		$ofst++;

		$bd["trackAsMenu"] = (int) $buttonFlagsStr[7];
		$bd["actionOffset"] = ui16dec(substr($tagData, $ofst, 2));
		$ofst += 2;

		$brLength = $bd["actionOffset"] ? strlen($tagData) - $bd["actionOffset"] : 0;
		$chLength = strlen($tagData) - ($bd["tagSL"] + 5) - $brLength - 1;
		$bd["chBuf"] = substr($tagData, $bd["tagSL"] + 5, $chLength); // strip END buttonRecord 0x00
		$bd["brBuf"] = substr($tagData, $bd["tagSL"] + 5, -$brLength);
	}
	else {
		/* TODO: implement defineButton fields: chBuf and brBuf */
		//$bd["chBuf"] =
	}

	//$brLength = strlen($brBuf);

	return $bd;
}

/**
 * reads data
 * returns an associative array with BUTTONRECORDS
 * fields
 */
function read_buttonRecord_data($buf, $btType = BUTTON){

	$brData = array();

	$brFlags = get_binData_string($buf[0]);

	$brData["hasBlendMode"]	  = (int) $brFlags[2];
	$brData["hasFilterList"]  = (int) $brFlags[3];
	$brData["stateHitTest"]	  = (int) $brFlags[4];
	$brData["stateDown"]	  = (int) $brFlags[5];
	$brData["stateOver"]	  = (int) $brFlags[6];
	$brData["stateUp"]		  = (int) $brFlags[7];

	$ofst = 1;
	$brData["characterID"]	  = ui16dec(substr($buf, $ofst, 2));
	$ofst += 2;
	$brData["placeDepth"]	  = ui16dec(substr($buf, $ofst, 2));
	$ofst += 2;

	$brData["placeMatrix"]	  = read_matrix_data(substr($buf, $ofst));
	$ofst += $brData["placeMatrix"]["matrixLength"];

	if($btType == BUTTON2){
		$brData["colorTransform"]  = read_cxform_with_alpha_data(substr($buf, $ofst));
		$ofst += $brData["colorTransform"]["cxformLength"];
	}

	if($brData["hasFilterList"]){
		$brData["filterList"] = read_filterList_data(substr($buf, $ofst));
		$ofst += $brData["filterList"]["length"];
	}
	if($brData["hasBlendMode"]){
		$brData["blendMode"] = 0; // TODO: FIX THIS, it is an arbitrary value
		//$brData["blendMode"] = ui8dec($buf[$ofst]);
		$ofst++;
	}

	$brData["length"] = $ofst;

	return $brData;
}


/**
 * reads data
 * fill an associative array with defineButtonSound fields
 */
function read_defineButtonSound_data($tagData = ""){

	if(!strlen($tagData)) return "";
	$bsd = read_tag_header_data(substr($tagData, 0, 10));
	$ofst = $bsd["tagSL"] + 2;

	$bsd["soundChar0"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;
	if($bsd["soundChar0"]){
		$bsd["soundInfo0"] = read_soundInfo_data(substr($tagData, $ofst));
		$ofst += $bsd["soundInfo0"]["siLength"];
	}

	$bsd["soundChar1"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;
	if($bsd["soundChar1"]){
		$bsd["soundInfo1"] = read_soundInfo_data(substr($tagData, $ofst));
		$ofst += $bsd["soundInfo1"]["siLength"];
	}

	$bsd["soundChar2"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;
	if($bsd["soundChar2"]){
		$bsd["soundInfo2"] = read_soundInfo_data(substr($tagData, $ofst));
		$ofst += $bsd["soundInfo2"]["siLength"];
	}
	$bsd["soundChar3"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;
	if($bsd["soundChar3"]){
		$bsd["soundInfo3"] = read_soundInfo_data(substr($tagData, $ofst));
		$ofst += $bsd["soundInfo3"]["siLength"];
	}

	return $bsd;
}


/**
 * reads data
 * fills an associative array with ScriptLimtis tag data
 */
function read_scriptLimits_data($tagData){

	$sld = array();
	$sld = read_tag_header_data(substr($tagData, 0, 10));
	$ofst = $sld["tagSL"] + 2;

	$sld["maxRecursionDepth"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;
	$sld["scriptTimeoutSeconds"] = ui16dec(substr($tagData, $ofst, 2));
	$ofst += 2;

	return $sld;
}

/**
 * reads data
 * fills an associative array with enableDebugger/2 tag data
 */
function read_enableDebugger_data($tagData){
	global $swfTagList;
	$edd = array();

	$edd = read_tag_header_data(substr($tagData, 0, 10));
	$ofst = $edd["tagSL"] + 2;
	$edd["debuggerVersion"] = DEBUGGER;

	if($edd["tagID"] == $swfTagList["EnableDebugger2"]){
		$ofst += 2;
		$edd["debuggerVersion"] = DEBUGGER2;
	}

	$edd["reserved"] = 0;			// always set even in useless cases
	$edd["MD5password"] = substr($tagData, $ofst, -1);

	return $edd;
}

//--------------------------------------------------
// WRITE functions
//--------------------------------------------------

/**
 * writes data
 * parameters, xmin, xmax, ymin, ymax
 * return a Flash rectangle structure
 */
function write_rect_data($xmin, $xmax, $ymin, $ymax, $inTwips = true) {

	$minBits = find_minBits(array($xmin, $xmax, $ymin, $ymax), $inTwips);

	if(!$inTwips){
		$xmin = nmb2twip($xmin);
		$xmax = nmb2twip($xmax);
		$ymin = nmb2twip($ymin);
		$ymax = nmb2twip($ymax);
	}

	$pad = strlen(get_pad(5 + $minBits * 4));

	return swf_bin(swf_value2bin($minBits, 5).
					swf_value2bin($xmin, $minBits).
					swf_value2bin($xmax, $minBits).
					swf_value2bin($ymin, $minBits).
					swf_value2bin($ymax, $minBits).
					swf_value2bin(0, $pad));
}

/**
 * write and returns rawdata
 * from a given associative array containing:
 * tagID -> tag code id
 * tagSL -> short or long type, if tagLength > 63 long tag header is written
 * tagLength -> body length
 * itemID -> item id, even if data does non contains
 */
function write_tag_header_data($tagHData){

	$tagBuf = "";

	if($tagHData["tagSL"] > 2 && $tagHData["tagLength"] < 63)
		$tagBuf = SWFTag_short_header($tagHData["tagID"], $tagHData["tagLength"]);
	else
		$tagBuf = SWFTag_long_header($tagHData["tagID"], $tagHData["tagLength"]);

	$tagBuf .= swf_word($tagHData["itemID"]);

	return $tagBuf;
}

/**
 * compose bindata from a given array
 * containing MATRIX fields:
 * [hasScale], [nScaleBits], scaleX, scaleY,
 * [hasRotate], [nRotateBits], rotateSkew0, rotateSkew1,
 * [nTranslateBits], translateX, nTranslateY
 */
function write_matrix_data($md = array()) {

	$buf = "";
	$bufStr = "";

	if(!is_array($md)) $md = array();

	if(!isset($md["scaleX"])) $md["scaleX"] = 1;
	if(!isset($md["scaleY"])) $md["scaleY"] = 1;
	if(!isset($md["hasScale"])) $md["hasScale"] = 0;
	if(!isset($md["translateX"])) $md["translateX"] = 0;
	if(!isset($md["translateY"])) $md["translateY"] = 0;
	if(!isset($md["rotateSkew0"])) $md["rotateSkew0"] = 0;
	if(!isset($md["rotateSkew1"])) $md["rotateSkew1"] = 0;

	if($md["hasScale"] || $md["scaleX"] != 1 || $md["scaleY"] != 1){

		//$md["scaleX"] *= 20;
		//$md["scaleY"] *= 20;

		$bufStr .= "1";
		$nScaleBits = fp_find_minBits(array($md["scaleX"], $md["scaleY"]));

		$nScaleBitsStr = swf_value2bin($nScaleBits, 5);
		$bufStr .= $nScaleBitsStr;

		$scaleXStr = fp_value2bin($md["scaleX"], $nScaleBits);
		$scaleYStr = fp_value2bin($md["scaleY"], $nScaleBits);
		$bufStr .= $scaleXStr;
		$bufStr .= $scaleYStr;
	}
	else $bufStr .= "0";

	if($md["rotateSkew0"] != 0 || $md["rotateSkew1"] != 0){

		$bufStr .= "1";

		$nRotateBits = fp_find_minBits(array($md["rotateSkew0"], $md["rotateSkew1"]));
		$nRotateBitsStr = swf_value2bin($nRotateBits, 5);
		$bufStr .= $nRotateBitsStr;

		$rs0Str = fp_value2bin($md["rotateSkew0"], $nRotateBits);
		$rs1Str = fp_value2bin($md["rotateSkew1"], $nRotateBits);
		$bufStr .= $rs0Str;
		$bufStr .= $rs1Str;
	}
	else $bufStr .= "0";

	if($md["translateX"] || $md["translateY"]) {

		$nTranslateBits = find_minBits(array($md["translateX"], $md["translateY"]));
		$nTranslateBitsStr = swf_value2bin($nTranslateBits, 5);
		$bufStr .= $nTranslateBitsStr;

		$bufStr .= swf_value2bin($md["translateX"], $nTranslateBits);
		$bufStr .= swf_value2bin($md["translateY"], $nTranslateBits);
	}
	else $bufStr .= "00000";

	$mLength = strlen($bufStr);
	//$pad = (8 - $mLength % 8) & 7;
	//$padStr = swf_value2bin(0, $pad);
	$bufStr .= get_pad($mLength);

	$bLength = strlen($bufStr) / 8;

	for($i = 0, $k = 0; $i < $bLength; $i++, $k += 8)
		$buf .= chr(bindec(substr($bufStr, $k, 8)));

	return $buf;
}


/**
 * write a COLOR structure from a given color array
 * containing these fields:
 * red, green, blue, [alpha]
 */
function write_color_data($cAr){

	$buf = "";

	$buf .= chr($cAr["red"]);
	$buf .= chr($cAr["green"]);
	$buf .= chr($cAr["blue"]);

	if(isset($cAr["alpha"])){
		$buf .= chr($cAr["alpha"]);
	}

	return $buf;

}


/**
 * write a CXFORM structure data from given associative array
 * containing these fields:
 * hasAddTerms, hasMultTerms, nBits, redMultTerm, greenMultTerm,
 * blueMultTerm, redAddTerm, greenAddTerm, blueAddTerm
 */
function write_cxform_data($cfd){

	$bufStr = "";

	$bufStr .= $cfd["hasAddTerms"] ? "1" : "0";
	$bufStr .= $cfd["hasMultTerms"] ? "1" : "0";

	$terms = array();
	$terms[] = isset($cfd["redMultTerms"])   ? $cfd["redMultTerms"]   : 0;
	$terms[] = isset($cfd["greenMultTerms"]) ? $cfd["greenMultTerms"] : 0;
	$terms[] = isset($cfd["blueMultTerms"])  ? $cfd["blueMultTerms"]  : 0;
	$terms[] = isset($cfd["redAddTerms"])    ? $cfd["redAddTerms"]    : 0;
	$terms[] = isset($cfd["greenAddTerms"])  ? $cfd["greenAddTerms"]  : 0;
	$terms[] = isset($cfd["blueAddTerms"])   ? $cfd["blueAddTerms"]   : 0;

	$nBits = find_minBits($terms);
	$bufStr .= swf_value2bin($nBits, 4);

	if($cfd["hasMultTerms"]){

		$bufStr .= swf_value2bin($cfd["redMultTerms"], $nBits);
		$bufStr .= swf_value2bin($cfd["greenMultTerms"], $nBits);
		$bufStr .= swf_value2bin($cfd["blueMultTerms"], $nBits);
	}

	if($cfd["hasAddTerms"]){

		$bufStr .= swf_value2bin($cfd["redAddTerms"], $nBits);
		$bufStr .= swf_value2bin($cfd["greenAddTerms"], $nBits);
		$bufStr .= swf_value2bin($cfd["blueAddTerms"], $nBits);
	}


	return get_binData($bufStr, true);
}


/**
 * write a CXFORMWITHALPHA structure data from given associative array
 * containing these fields:
 * hasAddTerms, hasMultTerms, nBits, redMultTerm, greenMultTerm
 * blueMultTerm, alphaMultTerm, redAddTerm, greenAddTerm
 * blueAddTerm, alphaAddTerm
 */
function write_cxform_with_alpha_data($cwad){

	$bufStr = "";

	$bufStr .= $cwad["hasAddTerms"] ? "1" : "0";
	$bufStr .= $cwad["hasMultTerms"] ? "1" : "0";

	$terms = array();
	$terms[] = isset($cwad["redMultTerms"])    ? $cwad["redMultTerms"]    : 0;
	$terms[] = isset($cwad["greenMultTerms"])  ? $cwad["greenMultTerms"]  : 0;
	$terms[] = isset($cwad["blueMultTerms"])   ? $cwad["blueMultTerms"]   : 0;
	$terms[] = isset($cwad["alphaMultTerms"])  ? $cwad["alphaMultTerms"]  : 0;
	$terms[] = isset($cwad["redAddTerms"])     ? $cwad["redAddTerms"]     : 0;
	$terms[] = isset($cwad["greenAddTerms"])   ? $cwad["greenAddTerms"]   : 0;
	$terms[] = isset($cwad["blueAddTerms"])    ? $cwad["blueAddTerms"]    : 0;
	$terms[] = isset($cwad["alphaAddTerms"])   ? $cwad["alphaAddTerms"]   : 0;

	$nBits = find_minBits($terms);
	$bufStr .= swf_value2bin($nBits, 4);

	if($cwad["hasMultTerms"]){

		$bufStr .= swf_value2bin($cwad["redMultTerms"], $nBits);
		$bufStr .= swf_value2bin($cwad["greenMultTerms"], $nBits);
		$bufStr .= swf_value2bin($cwad["blueMultTerms"], $nBits);
		$bufStr .= swf_value2bin($cwad["alphaMultTerms"], $nBits);
	}

	if($cwad["hasAddTerms"]){

		$bufStr .= swf_value2bin($cwad["redAddTerms"], $nBits);
		$bufStr .= swf_value2bin($cwad["greenAddTerms"], $nBits);
		$bufStr .= swf_value2bin($cwad["blueAddTerms"], $nBits);
		$bufStr .= swf_value2bin($cwad["alphaAddTerms"], $nBits);
	}

	return get_binData($bufStr, true);
}

/**
 * pack placeObject2 tagData
 * from a given associative array
 * containing these fields:
 * ------- please list fields --------
 */
function write_placeObject2_data($pod){

	global $swfTagList;
	$poBody = "";
	$binStr = "";

	/* TODO: implement packing tagData of placeObjec2 tag */

	$binStr .= $pod["pfHasClipActions"]		? "1" : "0";
	$binStr .= $pod["pfHasClipDepth"]		? "1" : "0";
	$binStr .= $pod["pfHasName"]			? "1" : "0";
	$binStr .= $pod["pfHasRatio"]			? "1" : "0";
	$binStr .= $pod["pfHasColorTransform"]	? "1" : "0";

	// a matrix (even empty) is always present
	$binStr .= "1";
	//$binStr .= $pod["pfHasMatrix"]		? "1" : "0";

	$binStr .= $pod["pfHasCharacter"]		? "1" : "0";
	$binStr .= $pod["pfMove"]				? "1" : "0";

	$poBody .= swf_bin($binStr);
	$poBody .= swf_word($pod["depth"]);

	if($pod["pfHasCharacter"])
		$poBody .= swf_word($pod["characterID"]);

	//if($pod["pfHasMatrix"])
	if(isset($pod["matrix"])) $poBody .= write_matrix_data($pod["matrix"]);
	else $poBody .= NIL;		// add empty matrix if not provided

	if($pod["pfHasColorTransform"])
		$poBody .= write_cxform_with_alpha_data($pod["colorTransform"]);

	if($pod["pfHasRatio"])
		$poBody .= swf_word($pod["ratio"]);

	if($pod["pfHasName"]){
		$poBody .= $pod["name"];
		$poBody .= NIL;
	}

	if($pod["pfHasClipDepth"])
		$poBody .= swf_word($pod["clipDepth"]);

	/* TODO implement clipActions {maybe}*/
	//if($pod["pfHasClipActions"]){
	//	$poBody .= write_clipActions_data($pod["clipActions"]);
	//}

	$tbLength = strlen($poBody);
	if($tbLength >= 63) $poHead = SWFTag_long_header($swfTagList["PlaceObject2"], strlen($poBody));
	else $poHead = SWFTag_short_header($swfTagList["PlaceObject2"], strlen($poBody));

	return $poHead .$poBody;
}

/**
 * >= swf8
 * pack placeObject3 tagData
 * from a given associative array
 * refer to write_placeObject_data
 *
 * it has some optional flags and fields:
 * 		$pfHasImage
 * 		$pfHasClassName
 * 		$pfHasCacheAsBitmap
 * 		$pfHasBlendMode
 * 		$pfHasFilterList
 *
 * 			their presence depends on placeflags Above
 * 		$className	// swf string
 * 		$filterList	// array of filter
 * 		$blendMode	// UI8

 */
function write_placeObject3_data($po3d){

	global $swfTagList;

	$poBody = "";
	$binStr = "";

	/* TODO: implement packing tagData of placeObjec3 tag */

	$binStr .= $po3d["pfHasClipActions"]	? "1" : "0";
	$binStr .= $po3d["pfHasClipDepth"]		? "1" : "0";
	$binStr .= $po3d["pfHasName"]			? "1" : "0";
	$binStr .= $po3d["pfHasRatio"]			? "1" : "0";
	$binStr .= $po3d["pfHasColorTransform"]	? "1" : "0";

	// a matrix (even empty) is always present
	$binStr .= "1";
	//$binStr .= $po3d["pfHasMatrix"]		? "1" : "0";

	$binStr .= $po3d["pfHasCharacter"]		? "1" : "0";
	$binStr .= $po3d["pfMove"]				? "1" : "0";

	// next three bits are reserved
	$binStr .= "000";

	$binStr .= $po3d["pfHasImage"]			? "1" : "0";
	$binStr .= $po3d["pfHasClassRef"] 		? "1" : "0";
	$binStr .= $po3d["pfHasCacheAsBitmap"] 	? "1" : "0";
	$binStr .= $po3d["pfHasBlendMode"] 		? "1" : "0";
	$binStr .= $po3d["pfHasFilterList"] 	? "1" : "0";

	$poBody .= swf_bin($binStr);
	$poBody .= swf_word($po3d["depth"]);

	if($po3d["pfHasClassRef"] || ($po3d["pfHasImage"] && $po3d["pfHasCharacter"]))
		$poBody .= swf_word($po3d["classRef"]);

	if($po3d["pfHasCharacter"] && $po3d["swfVersion"] < 9)
		$poBody .= swf_word($po3d["characterID"]);

	//if($po3d["pfHasMatrix"])
	if(isset($po3d["matrix"])) $poBody .= write_matrix_data($po3d["matrix"]);
	else $poBody .= NIL;		// add empty matrix if not provided

	if($po3d["pfHasColorTransform"])
		$poBody .= write_cxform_with_alpha_data($po3d["colorTransform"]);

	if($po3d["pfHasRatio"])
		$poBody .= swf_word($po3d["ratio"]);

	if($po3d["pfHasName"])
		$poBody .= swf_string($po3d["name"]);

	if($po3d["pfHasClipDepth"])
		$poBody .= swf_word($po3d["clipDepth"]);

	if($po3d["pfHasFilterList"])
		$poBody .= write_filterList_data($po3d["filterList"]);

	if($po3d["pfHasBlendMode"])
		$poBody .= chr($po3d["blendMode"]);

	/* TODO implement clipActions {maybe}*/
	//if($po3d["pfHasClipActions"]){
	//	$poBody .= write_clipActions_data($po3d["clipActions"]);
	//}

	$tbLength = strlen($poBody);
	$poHead = SWFTag_long_header($swfTagList["PlaceObject3"], strlen($poBody));

	return $poHead .$poBody;
}


/**
 * >= 8
 * pack hardcoded filter list
 */
function write_filterList_data($fld) {
	/* TODO not sure if this will be implemented */

	//$buf = chr(count($fld));
	//foreach($fld as $fd) $buf .= write_filter_data($fd);

	return "";
}

/**
 * >= 8
 * pack hardcoded single filter
 * NOTE: not sure if this will be implemented
 */
function write_filter_data($fd){
	/* TODO */

	return "";
}

/**
 * returns removeObject2 tagdata from given depth
 */
function write_removeObject2_data($depth){
	global $swfTagList;
	return SWFTag_short_header($swfTagList["RemoveObject2"], 2) .swf_word($depth);
}

/**
 * gets an associative array
 * returns setBackgroundColor tagdata
 */
function write_setBackgroundColor_data($cAr){

	global $swfTagList;

	$tagBody =  chr($cAr["red"]);
	$tagBody .= chr($cAr["green"]);
	$tagBody .= chr($cAr["blue"]);

	return SWFTag_short_header($swfTagList["SetBackgroundColor"], 3) .$tagBody;
}

/**
 * gets a string and return binary frame label OR named anchor tagdata
 */
function write_frameLabel_data($label = "", $hasAnchor = 0){
	global $swfTagList;
	$tagBody = swf_string($label);

	if($hasAnchor) $tagBody .= chr(1);

	return SWFTag_long_header($swfTagList["FrameLabel"], strlen($tagBody)) .$tagBody;
}

/**
 * gets a string representing MD5 password
 * returns protect tagdata
 */
function write_protect_data($MD5password){

	global $swfTagList;

	$tagBody = swf_word(0); // I don't know why, there are two null bytes before password
	$tagBody .= swf_string($MD5password);

	return SWFTag_short_header($swfTagList["Protect"], strlen($tagBody)) .$tagBody;
}

/**
 * writes data
 * from a given associative array
 * assetCount, number of assets
 * asset items contain fields: id, name
 */
function write_exportAssets_data($ead = array()){

	global $swfTagList;
	$tagBody = swf_word(count($ead["assets"]));

	foreach($ead["assets"] as $asset){
		$tagBody .= swf_word($asset["id"]);
		$tagBody .= swf_string($asset["name"]);
	}

	$tagHD = "";

	$tbLength = strlen($tagBody);

	// long header in any case
	$tagHD = SWFTag_long_header($swfTagList["ExportAssets"], $tbLength);

	return $tagHD .$tagBody;
}


// ----------------
// swf8
/**
 * writes data, compile binary tagData
 * from a given associative array
 */
function write_fileAttributes_data($faData = array()){

	global $swfTagList;

	$binStr = "000";
	$binStr .= $faData["containsMetaData"] ? "1" : "0";
	$binStr .= $faData["containsAS3"] ? "1" : "0";
	$binStr .= $faData["relativeURLs"] ? "1" : "0";
	$binStr .= $faData["suppressCDC"] ? "1" : "0";
	$binStr .= $faData["lpSecurity"] ? "1" : "0";

	$tagBody = swf_bin($binStr);
	$tagBody .= NIL;
	$tagBody .= swf_word(0);

	$tagHD = SWFTag_short_header($swfTagList["FileAttributes"], 4);

	return $tagHD .$tagBody;
}

/**
 * writes data, compile metadata tag froma a given metadata string
 */
function write_metadata_data($metadata = ""){

	global $swfTagList;
	$tagBody = swf_string($metadata);
	$tbLength = strlen($tagBody);
	$tagHD = SWFTag_long_header($swfTagList["MetaData"], $tbLength);

	return $tagHD .$tagBody;
}

/**
 * writes data
 * compile defineButton or defineButton2 tagData
 */
function write_defineButton_data($bd = array()){

	if(!count($bd)) return "";
	$tagData = "";

	$tagBody = swf_word($bd["itemID"]);
	$tagBody .= chr($bd["trackAsMenu"]);
	$tagBody .= swf_word($bd["actionOffset"]);

	foreach($bd["characters"] as $c){
		$tagBody .= write_buttonRecord_data($c);
	}

	$tagBody .= NIL;

	if(isset($bd["bcActions"]) && count($bd["bcActions"])){
		foreach($bd["bcActions"] as $bc){
			$tagBody .= write_buttonCondAction_data($bc, $bd["tagID"]);
		}
	}

	return SWFTag_long_header($bd["tagID"], strlen($tagBody)) .$tagBody;
}

/**
 * writes data
 * BUTTONRECORDS compiled binary data
 */
function write_buttonRecord_data($brd = array(), $btType = BUTTON2){

	$buf = "";

	$brFlags = "00";	// reserved;
	$brFlags .= $brd["hasBlendMode"]	 ? "1" : "0";
	$brFlags .= $brd["hasFilterList"] ? "1" : "0";
	$brFlags .= $brd["stateHitTest"]  ? "1" : "0";
	$brFlags .= $brd["stateDown"]	 ? "1" : "0";
	$brFlags .= $brd["stateOver"] 	 ? "1" : "0";
	$brFlags .= $brd["stateUp"] 		 ? "1" : "0";

	$buf .= swf_bin($brFlags);
	$buf .= swf_word($brd["characterID"]);
	$buf .= swf_word($brd["placeDepth"]);
	$buf .= write_matrix_data($brd["placeMatrix"]);
	$buf .= write_cxform_with_alpha_data($brd["colorTransform"]);

	if($brd["hasFilterList"]){
		$buf .= write_filterList_data($brd["filterList"]);
	}

	if($brd["hasBlendMode"]) $buf .= chr($brd["blendMode"]);

	return $buf;
}


/**
 * writes data
 * compile defineButton tagData from a given associative array
 */
function write_defineButtonSound_data($bsd = array()){

	if(!count($bsd)) return "";

	$tagBody = swf_word($bsd["itemID"]);
	$tagBody .= swf_word($bsd["soundChar0"]);
	if($bsd["soundChar0"]){ $tagBody .= write_soundInfo_data($bsd["soundInfo0"]); }

	$tagBody .= swf_word($bsd["soundChar1"]);
	if($bsd["soundChar1"]){ $tagBody .= write_soundInfo_data($bsd["soundInfo1"]); }

	$tagBody .= swf_word($bsd["soundChar2"]);
	if($bsd["soundChar2"]){ $tagBody .= write_soundInfo_data($bsd["soundInfo2"]); }

	$tagBody .= swf_word($bsd["soundChar3"]);
	if($bsd["soundChar3"]){ $tagBody .= write_soundInfo_data($bsd["soundInfo3"]); }

	return SWFTag_long_header(BUTTON_SOUND, strlen($tagBody)) .$tagBody;
}

/**
 * writes data
 * compile ScriptLimits tagData from a give associative array
 */
function write_scriptLimits_data($sld = array()){

	global $swfTagList;

	$tagBody = swf_word($sld["maxRecursionDepth"]);
	$tagBody .= swf_word($sld["scriptTimeoutSeconds"]);

	return SWFTag_short_header($swfTagList["ScriptLimits"], strlen($tagBody)) .$tagBody;
}


/**
 * writes data
 * compile EnableDebugger tagData from a give associative array
 */
function write_enableDebugger_data($edd = array()){
	global $swfTagList;

	$tagBody = swf_string($edd["MD5password"]);

	if($edd["debuggerVersion"] == DEBUGGER2) {
		$tagBody .= WORD0;
		return	SWFTag_short_header($swfTagList["EnableDebugger2"], strlen($tagBody)) .$tagBody;
	}
	else return SWFTag_short_header($swfTagList["EnableDebugger"], strlen($tagBody)) .$tagBody;
}


//----------------------------------------- end of mk_lib_swf
?>
